package fl.android;

import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

import android.media.AsyncPlayer;
import android.media.AudioManager;
import android.net.Uri;
import android.util.Log;
import fl.android.Note.Voice;

/**
 * Plays and renders audio
 * 
 */
public class Audio {

	static boolean stopPlaying = false;
	static final int SAMPLERATE = 8000;
	/*
	 * static final int BUFFERSIZE = AudioTrack.getMinBufferSize(SAMPLERATE,
	 * AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_8BIT);
	 * static AudioTrack audio = new AudioTrack(AudioManager.STREAM_MUSIC,
	 * SAMPLERATE, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_8BIT,
	 * BUFFERSIZE * 5, AudioTrack.MODE_STREAM); static { audio.play(); }
	 */

	static AsyncPlayer player = new AsyncPlayer("FLAndroid");

	/**
	 * saves the audio to a wave file.
	 * 
	 * @param file
	 *            a file to save audio to.
	 */
	static public void Save(File file) {
		assert (file.canWrite());

		Log.i("SAVE", file.canWrite() + "");

		try {
			int size = (Project.endPosition + 1) * Project.samplesPerPosition;
			byte[] renderedbytes = new byte[size]; // XXX: actually render bytes

			int sample = 0;
			while (sample < (Project.endPosition + 1)
					* Project.samplesPerPosition) {
				renderedbytes[sample] = renderSample(sample);
				sample++;
			}

			DataOutputStream fout = new DataOutputStream(new FileOutputStream(
					file));

			fout.write("RIFF".getBytes());
			fout.write(littleInt(36 + size));
			fout.write("WAVE".getBytes());
			fout.write("fmt ".getBytes());
			fout.write(littleInt(16));
			fout.write(littleShort(1));
			fout.write(littleShort(1)); // number of channels
			fout.write(littleInt(SAMPLERATE)); // sample rate
			fout.write(littleInt(SAMPLERATE * 1 * 8 / 8)); // byterate = sample
															// rate
															// * channels *
															// bits/sample / 8
			fout.write(littleShort(1 * 8 / 8)); // channels * bits/sample / 8
			fout.write(littleShort(8)); // bits/sample
			fout.write("data".getBytes());
			fout.write(littleInt(size));
			fout.write(renderedbytes);

			fout.flush();

			fout.close();
		} catch (Exception ex) {
			Log.wtf("SAVE", ex.toString());
		}

		assert (file.exists());
	}

	static private byte[] littleInt(int x) {
		return new byte[] { (byte) (x & 0xFF), (byte) ((x >> 8) & 0xFF),
				(byte) ((x >> 16) & 0xFF), (byte) ((x >> 24) & 0xFF) };
	}

	static private byte[] littleShort(int x) {
		return new byte[] { (byte) (x & 0xFF), (byte) ((x >> 8) & 0xFF) };
	}

	/**
	 * Plays the audio.
	 */
	private static File f = null;

	static public void Play() {
		player.stop();
		player = new AsyncPlayer("FLAndroid");
		if (f != null && f.exists() && f.canWrite())
			f.delete();
		try {
			f = File.createTempFile("tempaudio", ".wav");
			f.deleteOnExit();
		} catch (IOException e) {
			Log.wtf("PLAY", e.toString());
		}
		Save(f);
		Log.i("PLAY", f.exists() + " " + f.canRead());
		player.play(FLAndroidActivity.activity.getApplicationContext(),
				Uri.fromFile(f), false, AudioManager.STREAM_MUSIC);
	}

	public static void Stop() {
		player.stop();
		if (f != null && f.exists() && f.canWrite())
			f.delete();
	}

	/**
	 * Renders a single sample for use in play and save functions.
	 * 
	 * @param sampleNum
	 *            the sample number to render
	 * @return Sample with bit depth of 8
	 */
	static private byte renderSample(int sampleNum) {
		assert (sampleNum > 0);
		assert (sampleNum < Project.samplesPerPosition
				* (Project.endPosition + 1));
		int pos = sampleNum / Project.samplesPerPosition;
		double sample = 0;
		double t = (double) sampleNum / (double) SAMPLERATE;
		int count = 0;
		for (Note n : Project.notes.get(pos)) {
			switch (n.getvoice()) {
			case 1:// sine
				sample = sample + Math.sin(2. * Math.PI * n.getFrequency() * t);
				break;
			case 2:// saw
				sample = sample
						+ 2
						* (t * n.getFrequency()
								- Math.floor(t * n.getFrequency()) - 1 / 2.);
				break;
			case 3:// square
				sample = sample
						+ Math.signum(2 * (t * n.getFrequency()
								- Math.floor(t * n.getFrequency()) - 1 / 2.));
				break;
			case 4:// random
				sample = sample + Math.signum((t * n.getFrequency()
						- Math.floor(t * n.getFrequency()) - 1 / 2.)) * Math.random();
				break;
			}
			count++;
		}
		sample = sample / count;
		return (byte) Math.round(sample * Byte.MAX_VALUE);
	}
}
